﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using ACC.Data.Entities.Common;
using ACC.Extensions.Utilities;
using ACC.Util.Convert;

namespace ACC.Extensions.ClientBusiness.PLC
{
    public class SiemensPlcProcessForSvolt 
        : SiemensPlcProcess
    {

        private Thread _heartBeatThread;

        public override void Start()
        {
            base.Start();
            if (_heartBeatThread == null)
            {
                _heartBeatThread = new Thread(new ThreadStart(HeartBeatProcess));
                _heartBeatThread.IsBackground = true;
                _heartBeatThread.Name = "PLC Heartbeat Thread";
                _heartBeatThread.Start();
            }
        }

        public override void Stop()
        {
            base.Stop();
            if (_heartBeatThread != null && _heartBeatThread.ThreadState == ThreadState.Running)
            {
                _heartBeatThread.Join(5000);
                if (_heartBeatThread.IsAlive)
                    _heartBeatThread.Abort();
            }
            _heartBeatThread = null;
        }

        private void HeartBeatProcess()
        {
            while (true)
            {
                Thread.Sleep(5 * 1000);
                try
                {
                    if (IsConnected)
                    {
                        SendHeartBeat();
                    }
                }
                catch (Exception ex)
                {
                    Thread.Sleep(100);
                    Log4Helper.Error(ex.StackTrace);
                }
            }
        }

        private void SendHeartBeat()
        {
            int DBNumber = 190;
            int position = 4;
            int bitSite = 5;
            if (UISession.ClientSetting.EnablePLCDB1)
            {
                ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out DBNumber);
            }
            // 置位PartOK
            WriteBit(DBNumber, position, bitSite, true);
        }

        /// <summary>
        /// Load 成功写入信号
        /// </summary>
        public override void LoadSuccessfully()
        {
            int DBNumber = 190;
            int position = 6;
            if (UISession.ClientSetting.EnablePLCDB1)
            {
                ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out DBNumber);
            }
            // 置位PartOK
            WriteBit(DBNumber, position, 0, true);
            WriteBit(DBNumber, position, 1, false);
            WriteBit(DBNumber, position, 3, false);
        }

        public override void LoadFailed()
        {
            int DBNumber = 190;
            int position = 6;
            //WriteBit(DBNumber, position, bitSite, bValue);
            if (UISession.ClientSetting.EnablePLCDB1)
            {
                ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out DBNumber);
            }
            WriteBit(DBNumber, position, 0, false);
            WriteBit(DBNumber, position, 1, true);
            WriteBit(DBNumber, position, 3 , false);
        }


        public override void LoadByPass()
        {
            int DBNumber = 190;
            int position = 6;
            int bitSite = 2;
            //WriteBit(DBNumber, position, bitSite, bValue);
            if (UISession.ClientSetting.EnablePLCDB1)
            {
                ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out DBNumber);
            }
            WriteBit(DBNumber, position, 0, false);
            WriteBit(DBNumber, position, 1, false);
            WriteBit(DBNumber, position, 3, true);
        }

        /// <summary>
        /// 写入条码到PLC
        /// </summary>
        /// <param name="unitSN"></param>
        public override void WriteBarcodes(List<string> barCodes)
        {

            byte[] data = new byte[102 * barCodes.Count];

            for (var i = 0; i < barCodes.Count; i++)
            {
                data[i * 102] = 100;
                data[i * 102 + 1] = (byte)barCodes[i].Length;
                byte[] temp = Encoding.ASCII.GetBytes(barCodes[i]);
//                Array.Copy(temp, i * 102, data, i * 102 + 2, temp.Length);
                Array.Copy(temp, 0, data, i * 102 + 2, temp.Length);
            }
         

            int DBNumber = 193;
            int position = 2;
            if (UISession.ClientSetting.EnablePLCDB1)
            {
                ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out DBNumber);
                DBNumber += 3;
            }

            WriteBytes(DBNumber, position, data);
        }

        /// <summary>
        /// 换型的时候写入产品类型Id
        /// </summary>
        /// <param name="parttypeid"></param>
        public override void WriteParttypeID(int parttypeid)
        {
            int dbNumber = 192;
            int position = 58;
            if (UISession.ClientSetting.EnablePLCDB1)
            {
                ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out dbNumber);
                dbNumber += 2;
            }
            byte[] data = ObjectToBytes(ACCPLCUint16Convert((UInt16)parttypeid));
            WriteBytes(dbNumber, position, data);

        }

        public override bool WritePartTypeDataACC(Hashtable htPartTypeDataParam, bool clear)
        {
            try
            {
                int PartTypeDataIntLength = 16;
                int PartTypeDataRealLength = 70;
                //int ProcessDataStringDataLength = 30;
                SortedList listInt = (SortedList)htPartTypeDataParam["N_INT"];
                SortedList listReal = (SortedList)htPartTypeDataParam["N_REAL"];
                byte[] data = new byte[2 * PartTypeDataIntLength + 4 * PartTypeDataRealLength];
                int index = 0;
                byte[] temp;
                int dbNumber = 192;
                int position = 92;
                int position1 = 58;
                if (UISession.ClientSetting.EnablePLCDB1)
                {
                    ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out dbNumber);
                    dbNumber += 2;
                }
                //dbNumber = 80;
                // 写入前先清空
                if (clear)
                {
                    if (!WriteBytes(dbNumber, position, data))
                    {
                        return false;
                    }
                }

                for (int i = 1; i <= PartTypeDataIntLength; i++)
                {
                    if (listInt.Contains(i))
                    {
                        temp = BitConverter.GetBytes(Convert.ToInt16(listInt[i]));
                        Array.Reverse(temp);
                        Array.Copy(temp, 0, data, index, temp.Length);
                    }
                    index += 2;
                }
                for (int i = 1; i <= PartTypeDataRealLength; i++)
                {
                    if (listReal.Contains(i))
                    {
                        temp = BitConverter.GetBytes(ByteUtils.ACCPLCFloatConvert(Convert.ToSingle(listReal[i])));
                        Array.Copy(temp, 0, data, index, temp.Length);
                    }
                    index += 4;
                }
                return WriteBytes(dbNumber, position, data);
            }
            catch (Exception ex)
            {
                Log4Helper.Error(ex.StackTrace);
            }

            return false;
        }

        private int _palletStatus = -1;

        protected override bool PalletRequest(out bool bValue)
        {
            int DBNumber = 190;
            int position = 0;
            int bitSite = 7;

            if (UISession.ClientSetting.EnablePLCDB1)
            {
                ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out DBNumber);
            }

            byte[] buf = new byte[1];
            Driver.ReadBytesPLCDB(DBNumber, position, buf);
            BitArray arr = new BitArray(buf);

            bValue = arr[bitSite];

         
            if (_palletStatus == -1)
            {
                _palletStatus = bValue ? 1 : 0;
                return true;
            }


            if (_palletStatus == 1 && !arr[bitSite])
            {
                _palletStatus = 0;
                Log4Helper.Trace(string.Format("Pallet.Request|{0}.{1}.{2}|BOOL    ", DBNumber, position, bitSite) +
                                 arr[bitSite]);
                return true;
            }

            if (_palletStatus == 0 && arr[bitSite])
            {
                _palletStatus = 1;
                Log4Helper.Trace(string.Format("Pallet.Request|{0}.{1}.{2}|BOOL    ", DBNumber, position, bitSite) +
                                 arr[bitSite]);
                return true;
            }

            return false;
        }

        int _oldUnloadRequestBit = -1;
        public override bool UnloadRequest()
        {
            int DBNumber = 190;
            int position = 2;
            int bitSite = 4;
            byte[] buf = new byte[1];
            if (UISession.ClientSetting.EnablePLCDB1)
            {
                ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out DBNumber);
            }
            Driver.ReadBytesPLCDB(DBNumber, position, buf);
            BitArray arr = new BitArray(buf);

            int newValue = arr[bitSite] ? 1 : 0;
            if (_oldUnloadRequestBit == -1)
            {
                //第一次启动的时候避免做Unload
                _oldUnloadRequestBit = newValue;
                Log4Helper.Debug("unload request false 1111");
                return false;
            }

            if (_oldUnloadRequestBit != newValue)
            {
                Log4Helper.Trace(string.Format("Unload.Request|{0}.{1}.{2}|BOOL   ", DBNumber, position, bitSite) + arr[bitSite].ToString());
                if (_oldUnloadRequestBit == 0)
                {
                    _oldUnloadRequestBit = newValue;
                    Log4Helper.Debug("unload request true 1111");
                    return true;
                }

                _oldUnloadRequestBit = newValue;
                Log4Helper.Debug("unload request false 2222");
                return false;
            }
            Log4Helper.Debug("unload request false 3333");
            return false;
        }

        public override void GetProcData(ref List<ClientProcData> lstProcessData)
        {
            int DBNumber = 194;
            int position = 522;

            if (UISession.ClientSetting.EnablePLCDB1)
            {
                ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out DBNumber);
                DBNumber += 4;
            }

            try
            {
                byte[] data = new byte[50 * 4 * 3];
                var bRes = GetBytesByGroup(DBNumber, position, ref data);
                if (bRes)
                {
                    foreach (ClientProcData procData in lstProcessData)
                    {
                        byte[] temp = new byte[4];
                        int num = 0;
                        Regex regex = new Regex("REAL\\[(\\d+)\\]", RegexOptions.IgnoreCase);

                        var match = regex.Match(procData.Address);
                        if (match.Success)
                        {
                            num = int.Parse(match.Groups[1].Value);

                            Array.Copy(data, (num - 1) * 4, temp, 0, 4);
                            Array.Reverse(temp);
                            procData.Value = Convert.ToDouble(BitConverter.ToSingle(temp, 0).ToString());
                            Log4Helper.Info(procData.Address + ":" + procData.Value);
                        }
                    }
                }
                else
                {
                    Log4Helper.Error("read procdata failed");
                }
            }
            catch (Exception ex)
            {
                Log4Helper.Error("read procdata failed", ex);
            }
        }
        /// <summary>
        /// 分批次读processdata,一次读50个
        /// </summary>
        /// <param name="DBNumber"></param>
        /// <param name="position"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        private bool GetBytesByGroup(int DBNumber, int position, ref byte[] data)
        {
            bool bRes;
            if (data.Length > 50)
            {
                int nextPosition = position + 50;
                byte [] dataItem = new byte[50];
                bRes = Driver.ReadBytesPLCDB(DBNumber, position, dataItem);
                if (!bRes)
                {
                    throw new Exception("read 50 length failed");
                }

                byte[] restData = new byte[data.Length - 50];
                bRes = GetBytesByGroup(DBNumber, nextPosition, ref restData);
                if (!bRes)
                {
                    throw new Exception(string.Format("read rest item failed position {0}, length {1}", nextPosition, restData.Length));
                }
                Array.Copy(dataItem, 0, data, 0, 50);
                Array.Copy(restData, 0, data, 50, data.Length - 50);
                return bRes;
            }
            return  Driver.ReadBytesPLCDB(DBNumber, position, data);
        }

        public override void GetStatusBits(out int statusBit, out int failureBits)
        {
            Log4Helper.Debug("GetStatusBits start111");
            Log4Helper.Trace("GetStatusBits start111");

            int DBNumber = 194;
            int position = 514;
            statusBit = 0;
            failureBits = 0;
            if (UISession.ClientSetting.EnablePLCDB1)
            {
                ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out DBNumber);
                DBNumber += 4;
            }
            Log4Helper.Debug(string.Format("before read:statusBit|{0}.{1}|DINT-- {2}   ", DBNumber, position, statusBit));
            Log4Helper.Debug(string.Format("before read:failureBits|{0}.{1}|DINT-- {2}   ", DBNumber, position, failureBits));
            try
            {
                byte[] buf = new byte[8];
                bool bRes = Driver.ReadBytesPLCDB(DBNumber, position, buf);
                Log4Helper.Debug(string.Format("after read:statusBit|{0}.{1}|DINT-- {2} ,buf:{3} , ,bRes:{4} ", DBNumber, position, statusBit, buf,bRes));
                if (bRes)
                {
                    byte[] temp = new byte[4];
                    Array.Copy(buf, 0, temp, 0, 4);
                    Log4Helper.Debug(string.Format("194data-buf|{0}", buf));
                    Log4Helper.Debug(string.Format("194data-temp|{0}", temp));
                    Array.Reverse(temp);
                    statusBit = BitConverter.ToInt32(temp, 0);
                    Array.Copy(buf, 4, temp, 0, 4);
                    Array.Reverse(temp);
                    failureBits = BitConverter.ToInt32(temp, 0);
                    Log4Helper.Debug(string.Format("statusBit|{0}.{1}|DINT-- {2}   ", DBNumber, position, statusBit));
                    Log4Helper.Debug(string.Format("failureBits|{0}.{1}|DINT-- {2}   ", DBNumber, position, failureBits));
                    Log4Helper.Trace(string.Format("statusBit|{0}.{1}|DINT-- {2}   ", DBNumber, position,statusBit));
                    Log4Helper.Trace(string.Format("failureBits|{0}.{1}|DINT-- {2}   ", DBNumber, position, failureBits));
                    Log4Helper.Debug("GetStatusBits end222");
                    Log4Helper.Trace("GetStatusBits end222");
                }
                else
                {
                    Log4Helper.Debug(string.Format("read statusBit|{0}.{1}|DINT-- fail {2}   ", DBNumber, position, statusBit));
                    Log4Helper.Debug(string.Format("read failureBits|{0}.{1}|DINT-- fail {2}   ", DBNumber, position, failureBits));
                    Log4Helper.Debug("read db fail");
                    

                }
            }
            catch (Exception ex)
            {
                Log4Helper.Error("read procdata failed", ex);
            }
        }

        public override void UnloadSuccessfully(int result)
        {
            int DBNumber = 190;
            int position = 6;
            int bitSite = 4;
            if (UISession.ClientSetting.EnablePLCDB1)
            {
                ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out DBNumber);
            }
            // 置位PartOK
            WriteBit(DBNumber, position, bitSite, true);
            WriteBit(DBNumber, position, bitSite + 1, false);
        }

        public override void UnloadFailed()
        {
            int DBNumber = 190;
            int position = 6;
            int bitSite = 5;
            if (UISession.ClientSetting.EnablePLCDB1)
            {
                ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out DBNumber);
            }
            // 置位PartOK
            WriteBit(DBNumber, position, bitSite - 1, false);
            WriteBit(DBNumber, position, bitSite, true);
        }

        private bool _bFaultRequest = false;
        protected override bool FaultRequest()
        {
            int DBNumber = 190;
            int position = 0;
            int bitSite = 0;
            byte[] buf = new byte[1];
            if (UISession.ClientSetting.EnablePLCDB1)
            {
                ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out DBNumber);
            }
            Driver.ReadBytesPLCDB(DBNumber, position, buf);
            BitArray arr = new BitArray(buf);

            if (_bFaultRequest != arr[bitSite])
            {
                Log4Helper.Trace(string.Format("Fault.Request|{0}.{1}.{2}|BOOL   ", DBNumber, position, bitSite) + arr[bitSite].ToString());
                if (_bFaultRequest == false)
                {
                    _bFaultRequest = arr[bitSite];
                    return true;
                }

                _bFaultRequest = arr[bitSite];
                return false;
            }

            return false;
        }

        private bool _bTipRequest = false;
        protected override bool TipRequest()
        {
            int DBNumber = 190;
            int position = 0;
            int bitSite = 5;
            byte[] buf = new byte[1];
            if (UISession.ClientSetting.EnablePLCDB1)
            {
                ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out DBNumber);
            }
            Driver.ReadBytesPLCDB(DBNumber, position, buf);
            BitArray arr = new BitArray(buf);

            if (_bTipRequest != arr[bitSite])
            {
                Log4Helper.Trace(string.Format("Tip.Request|{0}.{1}.{2}|BOOL   ", DBNumber, position, bitSite) + arr[bitSite].ToString());
                if (_bTipRequest == false)
                {
                    _bTipRequest = arr[bitSite];
                    return true;
                }

                _bTipRequest = arr[bitSite];
                return false;
            }

            return false;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct SiemensString_80
        {
            public byte MaxLength;
            public byte Length;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 80)]
            public byte[] strData;
        }

        public virtual String ComponentStringToString(byte[] strarr, byte Length)
        {
//            char[] datas = new char[strarr.Length];
            byte[] datas = new byte[Length];
            Array.Copy(strarr, datas, Length);

            return Encoding.Default.GetString(datas);

//            String tmp = new String(datas);

//            if (Length < tmp.Length)
//                return tmp.Substring(0, Length);
//            else
//                return tmp;
        }

        public Type _BarcodeStringType = typeof(SiemensString_80);

        public override void GetFaultMessage(out int code, out string msg)
        {
            int DBNumber = 197;
            int position = 0;
            int bitSite = 0;
            ReadCodeAndMessage(out code, out msg, DBNumber, position);
        }

        private void ReadCodeAndMessage(out int code, out string msg, int DBNumber, int position)
        {
            byte[] buf = new byte[86];
            code = 0;
            msg = "";
            if (UISession.ClientSetting.EnablePLCDB1)
            {
                ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out DBNumber);
                DBNumber += 7;
            }

            Driver.ReadBytesPLCDB(DBNumber, position, buf);

            try
            {
                bool bRes = Driver.ReadBytesPLCDB(DBNumber, position, buf);
                if (bRes)
                {
                    byte[] temp = new byte[4];
                    Array.Copy(buf, 0, temp, 0, 4);
                    Array.Reverse(temp);
                    code = BitConverter.ToUInt16(temp, 0);

                    temp = new byte[82];
                    Array.Copy(buf, 4, temp, 0, 82);
                    object obj = ByteUtils.BytesToObject(temp, _BarcodeStringType);
                    msg = ComponentStringToString(((SiemensString_80) obj).strData, ((SiemensString_80) obj).Length);
                }
            }
            catch (Exception ex)
            {
                Log4Helper.Error("read procdata failed", ex);
            }
        }


        public override void GetTipMessage(out string msg,out bool alarm)
        {
            int DBNumber = 197;
            int position = 86;
            int bitSite = 0;

            msg = "";
            alarm = false;
            if (UISession.ClientSetting.EnablePLCDB1)
            {
                ResolvePlcDbNumber(UISession.ClientSetting.PLCDBAddress1, out DBNumber);
                DBNumber += 7;
            }
           
            try
            {
                byte[] buf = new byte[14];
                bool bRes = Driver.ReadBytesPLCDB(DBNumber, position, buf);
                if (bRes)
                {
                    object obj = ConvertTipMessageFromBytes(buf);
                    msg = ToMessage((TipMessage) obj,out alarm);

                    
                    
                }
            }
            catch (Exception ex)
            {
                Log4Helper.Error("read procdata failed", ex);
            }

        }

        private TipMessage ConvertTipMessageFromBytes(byte[] buf)
        {
            TipMessage tipMessage = new TipMessage();
            tipMessage.Code = BitConverter.ToUInt16(GetBytes(buf, 0, 2), 0);
            tipMessage.Num = BitConverter.ToUInt16(GetBytes(buf, 2, 2), 0);
            tipMessage.Result = BitConverter.ToUInt16(GetBytes(buf, 4, 2), 0);
            tipMessage.Torque = BitConverter.ToSingle(GetBytes(buf, 6, 4), 0);
            tipMessage.Angel = BitConverter.ToSingle(GetBytes(buf, 10, 4), 0);
            return tipMessage;
        }

        private byte[] GetBytes(byte[] buf, int startIndex, int length)
        {
            byte[] temp = new byte[length];
            Array.Copy(buf, startIndex, temp, 0, length);
            Array.Reverse(temp);
            return temp;
        }

        private string ToMessage(TipMessage tipMessage,out bool alarm)
        {
            var codeMessage = GetCodeRelatedMessage(tipMessage.Code);
//            Log4Helper.Trace(string.Format("Tip.code:{0},Tip.Num:{1},Tip.result:{2}}, 扭矩[{3}], 角度[{4}] ", 
//                                 tipMessage.Code, tipMessage.Num, tipMessage.Result, tipMessage.Torque, tipMessage.Angel));
            
            alarm = false;
            if (tipMessage.Result==2)
            {
                alarm = true;
            }
           
            if (tipMessage.Num == 0 && tipMessage.Code!=0)
            {
//                return "PLC信息:切换螺丝程序";
                return string.Format("PLC信息: {0},注意切换螺丝程序", codeMessage);
            }

//            if (tipMessage.Num == 0)
//            {
//                return "拧紧开始";
//            }

            return string.Format("PLC信息: {0},第{1}个螺丝{2}, 扭矩[{3}], 角度[{4}]", codeMessage, tipMessage.Num,
                tipMessage.Result == 1 ? "成功" : "失败", tipMessage.Torque, tipMessage.Angel);
        }

        public readonly Dictionary<ushort, string> PromptMessage = new Dictionary<ushort, string>
        {
            {100, "前挡板固定"},
            {110, "模组紧固"},
            {120, "BAR片安装"},
            {200, "风扇"},
            {210, "正负极接插件"},
            {220, "BMU"},
            {230, "通讯接头"},
            {240, "正负连接排"},
            {300, "采集线定扭"},
            {310, "装前面板"},
            {320, "串联铜排"},
        };

        private object GetCodeRelatedMessage(ushort tipMessageCode)
        {
            if (PromptMessage.ContainsKey(tipMessageCode))
            {
                return PromptMessage[tipMessageCode];
            }

            return "";
        }
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    public struct TipMessage
    {
        public UInt16 Code;
        public UInt16 Num;
        public UInt16 Result;

        public Single Torque;
        public Single Angel;
    }


}
